﻿//using FubarDev.FtpServer;
//using FubarDev.FtpServer.AccountManagement;
//using FubarDev.FtpServer.FileSystem.DotNet;
using DotNetCore.CAP.Dashboard;
using FluentFTP;
using Hangfire;
using Hangfire.Console;
using Hangfire.Dashboard.BasicAuthorization;
using Hangfire.Heartbeat;
using Hangfire.Heartbeat.Server;
using Hangfire.Mongo;
using Hicap.QRCodeWarehouse.Processor.Consul;

//using Hicap.QRCodeWarehouse.Processor.FtpServer;
using Hicap.QRCodeWarehouse.Processor.Manager;
using Hicap.QRCodeWarehouse.Processor.Service;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
using Serilog;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;

namespace Hicap.QRCodeWarehouse.Processor
{
    /// <summary>
    /// Startup
    /// </summary>
    /// <remarks>
    /// Reference
    /// Netcore 2.1:https://www.microsoft.com/net
    /// AutoMapper:http://automapper.org/
    /// CAP:https://www.github.com/dotnetcore/cap
    /// Mongodb C# Driver:https://docs.mongodb.com/ecosystem/drivers/csharp/
    /// FileHelp:https://www.filehelpers.net/
    /// FubarDev.FtpServer:https://github.com/FubarDevelopment/FtpServer
    /// HangFire:https://www.hangfire.io/
    /// SeriLog:https://serilog.net/
    /// SharpLibZip:https://github.com/icsharpcode/SharpZipLib
    /// </remarks>
    public class Startup
    {
        private static IConfiguration Configuration { get; set; }
        private string MongoConnectionString { get; }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="configuration"></param>
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
            //Monogo ConnectionString
            MongoConnectionString = Configuration.GetConnectionString("Hicap.QRCodeWarehouse.Processor");
            //MySQL ConnectionString
            //            MySqlConnectionString = Configuration.GetConnectionString(
            //                "Hicap.QRCodeWarehouse");
        }

        /// <summary>
        /// ConfigureServices
        /// </summary>
        /// <param name="services"></param>
        public void ConfigureServices(IServiceCollection services)
        {
            //Logging Configration
            AddLogging(services);

            //RegisterServices
            AddRegisterServices(services);

            //Consul Configration
            AddConsul(services);

            //Cap Configration
            AddCap(services);

            //HangFire Configration
            AddHangfire(services);

            // Mvc Configration
            AddMvc(services);

            //FtpServer Configration
            //            AddFtpServer(services);
            AddFtpClient(services);
        }

        /// <summary>
        /// Mvc Configration
        /// </summary>
        /// <param name="services"></param>
        private static void AddMvc(IServiceCollection services)
        {
            services.AddMvcCore();
        }

        /// <summary>
        /// HangFire Configration
        /// </summary>
        /// <param name="services">IServiceCollection</param>
        private void AddHangfire(IServiceCollection services)
        {
            services.AddHangfire(config =>
            {
                config.UseHeartbeatPage(checkInterval: TimeSpan.FromSeconds(1));
                //use Console
                config.UseConsole();
                //use MongoStorage
                config.UseMongoStorage(MongoConnectionString, "hangfire", new MongoStorageOptions()
                {
                    MigrationOptions = new MongoMigrationOptions
                    {
                        Strategy = MongoMigrationStrategy.Migrate,
                        BackupStrategy = MongoBackupStrategy.Collections
                    }
                });
                //                config.UseStorage(new MySqlStorage(MySqlConnectionString));
            });
        }

        /// <summary>
        /// Configure Consul
        /// </summary>
        /// <param name="services">IServiceCollection</param>
        private void AddConsul(IServiceCollection services)
        {
            services.AddConsul(x =>
            {
                x.WithHost(
                        $"http://{Configuration["ConsulConfiguration:Host"]}:{Configuration["ConsulConfiguration:Port"]}");
            });
        }

        //        /// <summary>
        //        /// FtpServer Configration
        //        /// </summary>
        //        /// <param name="services">IServiceCollection</param>
        //        private static void AddFtpServer(IServiceCollection services)
        //        {
        //            services.AddSingleton<IFtpServerExtend, FtpServerExtend>();
        //            services.AddSingleton<IMembershipProvider, BasicUserMembershipProvider>();
        //
        //            //Configure dotnet filesystem , with ~\FtpFiles directory
        //            services.Configure<DotNetFileSystemOptions>(opt =>
        //            {
        //                opt.RootPath = Path.Combine(Directory.GetCurrentDirectory(), "FtpFiles");
        //            });
        //
        //            //use dotnet filesystem
        //            services.AddFtpServer(builder =>
        //            {
        //                // Use the .NET file system functionality
        //                builder.UseDotNetFileSystem();
        //            });
        //            services.Configure<FtpConnectionOptions>(opt =>
        //                {
        //                    opt.DefaultEncoding = Encoding.ASCII;
        //                });
        //            //Configure server address:port
        //            services.Configure<FtpServerOptions>(opt =>
        //            {
        //                opt.ServerAddress = Configuration["FtpServer:HostAddress"];
        //                opt.Port = Convert.ToInt32(Configuration["FtpServer:Port"]);
        //            });
        //        }

        /// <summary>
        /// Cap Configration
        /// </summary>
        /// <param name="services">IServiceCollection</param>
        /// <remarks>
        ///CAP is a library based on .Net standard, which is a solution to deal with distributed transactions, also has the function of EventBus, it is lightweight, easy to use, and efficiently.
        /// </remarks>
        private void AddCap(IServiceCollection services)
        {
            services.AddCap(x =>
            {
                x.SucceedMessageExpiredAfter = 12 * 3600;
                //Retry 3
                x.FailedRetryCount = 3;
                //Retry Interval 60s
                x.FailedRetryInterval = 60;
                //Dashboard
                x.UseDashboard(y =>
                {
                    y.Authorization = new List<IDashboardAuthorizationFilter>()
                    {
                        new CapAuthorizationFilter()
                        {
                            LoginCaseSensitive = true,
                            Users = new[]
                            {
                                new BasicAuthAuthorizationUser
                                {
                                    Login = Configuration["DefaultAuthorizationUser:UserName"],
                                    PasswordClear = Configuration["DefaultAuthorizationUser:Password"],
                                }
                            }
                        }
                    };
                });
                x.UseDiscovery(y =>
                {
                    string sysServiceName = Environment.GetEnvironmentVariable("HicapServiceName");

                    if (string.IsNullOrEmpty(sysServiceName))
                        sysServiceName = Configuration["ConsulConfiguration:ServerName"];

                    if (string.IsNullOrEmpty(sysServiceName))
                        sysServiceName = Configuration["SelfHost:Host"];

                    int nodeId = Convert.ToInt32(sysServiceName.Substring(sysServiceName.Length - 1)) + 400;

                    y.DiscoveryServerHostName = Configuration["ConsulConfiguration:Host"];
                    y.DiscoveryServerPort = Convert.ToInt32(Configuration["ConsulConfiguration:Port"]);
                    y.CurrentNodeHostName = sysServiceName;
                    y.CurrentNodePort = Convert.ToInt32(Configuration["SelfHost:Port"]);
                    y.NodeId = nodeId;
                    y.NodeName = $"Hicap Node - {sysServiceName}";
                });

                x.UseMongoDB(MongoConnectionString);

                //Mysql
                //                x.UseMySql(MySqlConnectionString);

                //Rabbitmq
                x.UseRabbitMQ(y =>
                {
                    y.HostName = Configuration["RabbitMq:Host"];
                    y.Port = Convert.ToInt32(Configuration["RabbitMq:Port"]);
                    y.UserName = Configuration["RabbitMq:UserName"];
                    y.Password = Configuration["RabbitMq:Pwd"];
                    y.QueueMessageExpires = 259200000;
                });

                x.FailedThresholdCallback = (type, topic, content) =>
                {
                    Log.Error($"!-- CAP Message Call Back ERROR -- [{type.ToString()}][{topic}][{content}]");
                };
            });
        }

        private void AddFtpClient(IServiceCollection services)
        {
            services.AddSingleton<FtpClient>(x => new FtpClient()
            {
                Host = Configuration["Ftp:HostAddress"],
                Port = Convert.ToInt32(Configuration["Ftp:Port"]),
                Credentials = new NetworkCredential(Configuration["DefaultAuthorizationUser:UserName"], Configuration["DefaultAuthorizationUser:Password"])
            });
        }

        /// <summary>
        /// Serilog Configration
        /// </summary>
        /// <param name="services">IServiceCollection</param>
        private static void AddLogging(IServiceCollection services)
        {
            string logPth = Path.Combine(Directory.GetCurrentDirectory(), "logs");

            Serilog.Core.Logger logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
                .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Information)
                .MinimumLevel.Override("System", LogEventLevel.Information)
                .MinimumLevel.Override("DotNetCore", LogEventLevel.Information)
                .WriteTo.Console(LogEventLevel.Debug)
                .WriteTo.File(Path.Combine(logPth, "info", "log-.log"), rollingInterval: RollingInterval.Day, restrictedToMinimumLevel: LogEventLevel.Information)
                .WriteTo.File(Path.Combine(logPth, "error", "log-.log"), rollingInterval: RollingInterval.Day,
                    restrictedToMinimumLevel: LogEventLevel.Warning).CreateLogger();

            Log.Logger = logger;

            services.AddLogging(x =>
            {
                x.SetMinimumLevel(LogLevel.Debug);
                x.AddSerilog(logger);
            });
        }

        /// <summary>
        /// Ms Dependency Injection
        /// </summary>
        /// <param name="services">IServiceCollection</param>
        private void AddRegisterServices(IServiceCollection services)
        {
            services.AddSingleton<IMongoClient>(x =>
                new MongoClient(MongoConnectionString));
            services.AddTransient<IMonitorManager, MonitorManager>();
            services.AddTransient<ICodeGenManager, CodeGenManager>();
            services.AddTransient<ICodeGenService, CodeGenService>();
            services.AddTransient<ICodeTakenManager, CodeTakenManager>();
            services.AddTransient<ICodeTakenService, CodeTakenService>();
            services.AddTransient<IMonitorService, MonitorService>();
        }

        /// <summary>
        /// Bootstrap Configure
        /// </summary>
        /// <param name="app"></param>
        /// <param name="env"></param>
        /// <param name="lifetime"></param>
        /// <remarks>
        /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        /// </remarks>
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHangfireServer(additionalProcesses: new[] { new SystemMonitor(TimeSpan.FromSeconds(1)) });
            app.UseHangfireDashboard("/hangfire", new Hangfire.DashboardOptions
            {
                Authorization = new[] { new BasicAuthAuthorizationFilter(new BasicAuthAuthorizationFilterOptions
                {
                    RequireSsl = false,
                    SslRedirect = false,
                    LoginCaseSensitive = true,
                    Users = new []
                    {
                        new BasicAuthAuthorizationUser
                        {
                            Login = Configuration["DefaultAuthorizationUser:UserName"],
                            PasswordClear =  Configuration["DefaultAuthorizationUser:Password"],
                        }
                    }
                }) }
            });
            app.UseConsul(lifetime, option =>
            {
                string sysServiceName = Environment.GetEnvironmentVariable("HicapServiceName");

                if (string.IsNullOrEmpty(sysServiceName))
                    sysServiceName = Configuration["ConsulConfiguration:ServerName"];

                if (string.IsNullOrEmpty(sysServiceName))
                    sysServiceName = Configuration["SelfHost:Host"];

                option.WithSelfHost($"http://{sysServiceName}:{Configuration["SelfHost:Port"]}");
                option.WithServerName(sysServiceName);
            });

            app.UseMvcWithDefaultRoute();
            //            app.UseFtpServer();
        }
    }
}